package wator.model.topologies;

import wator.model.IntegerVector;
import wator.model.Topology;

/**
 * This topology is a more general version of a cylinder topology, in which
 * the universe is connected only regarding few dimensions. For more 
 * information about how to define these "cut" dimensions under
 * {@link #CylinderTopology(IntegerVector)}.
 */
public class CylinderTopology extends Topology {

	/* (non-Javadoc)
	 * The cutVector is described in the constructor javadocs.
	 */
	private IntegerVector cutVector;
	
	/* (non-Javadoc)
	 * The reduce vector is used in the distanceVector() method. It is equal to
	 * the size vector, except for the fact that in cutVector directions, it
	 * is multiplied by 2 so that no reduction is possible in this direction.
	 */
	private IntegerVector reduceVector;
	
	/**
	 * Sole constructor. Defines a cylinder like topology. If the given
	 * <code>cutVector</code> is a unit vector, the universe is not connected
	 * in this dimension. Therefore, <code>Integer2D(0, 1)</code> would create
	 * a geometric cylinder topology. If more than one elements of the 
	 * <code>cutVector</code> are set to 1, the universe won't be connected in
	 * all thus dimensions. If <code>cutVector</code> is set to 0, a torus 
	 * topology is induced.
	 * 
	 * @param cutVector the cut vector
	 * @throws NullPointerException if <code>cutVector</code> is null
	 */
	public CylinderTopology (IntegerVector cutVector) {
		this.cutVector = cutVector.div(cutVector.max());
	}
	
	/* (non-Javadoc)
	 * @see wator.model.Topology#apply(wator.model.IntegerVector)
	 */
	@Override
	public IntegerVector apply(IntegerVector v) {
		// Cut?
		IntegerVector projection = v.mul(cutVector);
		if (projection.min() < 0 || sizeVector.sub(projection).min() < 0) {
			return null;
		}
		return v.add(sizeVector).mod(sizeVector);
	}

	/* (non-Javadoc)
	 * @see wator.model.Topology#distanceVector(wator.model.IntegerVector, wator.model.IntegerVector)
	 */
	@Override
	public IntegerVector distanceVector(IntegerVector v1, IntegerVector v2) {
		return v2.sub(v1).reduce(reduceVector);
	}

	/**
	 * Set the size vector of this topology and adjusts
	 * <code>reduceVector</code>.
	 * 
	 * @param sizeVector the new size vector
	 */
	@Override
	public void setSizeVector(IntegerVector sizeVector) {
		super.setSizeVector(sizeVector);
		reduceVector = cutVector.mul(sizeVector).add(sizeVector);
	}
}
